package com.pi.tcp.handler;

import java.net.SocketAddress;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.ChannelHandler.Sharable;

import com.alibaba.fastjson.JSON;
import com.pi.tcp.util.PiBedInsEnum;
import com.pi.base.enumerate.charset.CommCharset;
import com.pi.base.enumerate.redis.RedisCacheEnum;
import com.pi.base.util.cache.RedisUtil;
import com.pi.base.util.lang.ByteUtil;
import com.pi.comm.sequence.SnowflakeFactory;
import com.pi.smart.client.ins.comm.domain.BaseInsMessage;

/**
 * 小棉被数据接收
 * 
 * @author chenmf@163.com
 * @date 2020年07月15日01:41:49
 */
@Sharable
public class LockHander extends ChannelHandlerAdapter {

    private static Logger logger = LoggerFactory.getLogger(LockHander.class);
    public static byte[] defaultWifiIns = ByteUtil.hexToByte("76 0D 3C 2A 45 57 0C 0B 14 01 09 57 41 4e 47 42 49 4e 42 4f 08 31 32 33 34 35 36 37 38 03");

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object baseMsg) throws Exception {
        SocketAddress remoteAddress = (null != ctx.channel()) ? ctx.channel().remoteAddress() : null;
        if (null != baseMsg) {
            if (baseMsg instanceof DatagramPacket) {
                DatagramPacket packet = (DatagramPacket) baseMsg;
                logger.info("接收到未知的UDP数据: {}", packet.content());
            } else if (baseMsg instanceof ByteBuf) {
                ByteBuf message = (ByteBuf) baseMsg;
                byte[] bytes = new byte[message.readableBytes()];
                message.readBytes(bytes);
                logger.info("原始数据：{}", ByteUtil.byteToHex(bytes));
                String deviceCode;
                if ((bytes[0] & 0xFF) == 0xFE && ((bytes[1] & 0xFF) == 0x01 || (bytes[1] & 0xFF) == 0x09)) {
                    byte cmd = bytes[2];//FE 01 01 08 00 01 10 20 30 40 50 60 70 80 11 22
                    byte[] devCode = new byte[8];
                    System.arraycopy(bytes, 5, devCode, 0, 8);
                    deviceCode = ByteUtil.byteToHex(devCode, false);
                    if ((cmd & 0xFF) == 0x01) {
                        logger.info("发送时间同步： {}", ByteUtil.byteToHex(PiBedInsEnum.generateTimeIns()));
                        ctx.writeAndFlush(Unpooled.copiedBuffer(PiBedInsEnum.generateTimeIns()));
                        Thread.sleep(2000);
                        logger.info("WIFI配网： {}", ByteUtil.byteToHex(devCode));

                        String wifiPwdIns =
                                RedisUtil.get(RedisCacheEnum.SMART_DEVICE_WIFI, new Object[] {ByteUtil.byteToHex(devCode, false)});
                        if (StringUtils.isNotBlank(wifiPwdIns)) {
                            List<String> wifiArr = JSON.parseArray(wifiPwdIns, String.class);
                            if (null == wifiArr || wifiArr.size() != 2) {
                                logger.error("指令接受错误: {}", wifiArr);
                            }
                            logger.info("发送wifi指令： {}", ByteUtil.byteToHex(PiBedInsEnum.generateWifiIns(wifiArr.get(0), wifiArr.get(1), "")));
                            ctx.writeAndFlush(Unpooled.copiedBuffer(PiBedInsEnum.generateWifiIns(wifiArr.get(0), wifiArr.get(1), "")));
                        }
                    } else if ((cmd & 0xFF) == 0xF6) {
                        //FE 09 F6 09 00 01 10 20 30 40 50 60 01 11 22
                        logger.info("是否配置成功  " + (bytes[12] & 0xFF));
                    } else if ((cmd & 0xFF) == 0x02) {
                        logger.info("接收上报成功： {}", ByteUtil.byteToHex(PiBedInsEnum.generateUploadSuccessIns()));
                        ctx.writeAndFlush(Unpooled.copiedBuffer(PiBedInsEnum.generateUploadSuccessIns()));
                    }
                } else {
                    deviceCode = handleOldCmd(bytes, ctx);
                }

                if (null != deviceCode) {
                    BaseInsMessage cmdMessage = new BaseInsMessage();
                    cmdMessage.setCmd(bytes);
                    cmdMessage.setDevCode(deviceCode);
                    cmdMessage.setMessageId(SnowflakeFactory.getDefaultWorker().nextId());
                    cmdMessage.setTs(System.currentTimeMillis());
                    RedisUtil.rpush(RedisCacheEnum.SMART_DEVICE_CMD_COMMON, cmdMessage);
                }
            } else {
                logger.error("接收参数错误,当前接收到的数据类型为-{},接收到的端口为：{}",
                        baseMsg.getClass().getName(), (null != remoteAddress) ? remoteAddress.toString() : "");
            }
        }
        logger.debug("释放连接 - {}", (null != remoteAddress) ? remoteAddress.toString() : "");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        ctx.close();
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
//		ctx.flush();
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
//	  ctx.close();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        String message = null != cause.getMessage() ? cause.getMessage() : "";
        //String localMsg = cause.getLocalizedMessage();
        Channel channel = ctx.channel();
        // SocketAddress local = channel.localAddress();
        SocketAddress remote = channel.remoteAddress();
        //ChannelMetadata metadata = channel.metadata();
        if (message.indexOf("Connection timed out") >= 0 || message.indexOf("Connection reset") >= 0 || "远程主机强迫关闭了一个现有的连接。".equals(message)) {
            logger.error("远程主机-{}-断开了连接", remote);
            //尝试清空channel
            channel.flush();
            //当错误发生时，尝试调用垃圾回收, 不知道是否可以减少Direct Memory 泄露的错误,需要验证
//      System.gc();
        } else {
            logger.error(cause.getMessage(), cause);
        }
        ctx.close();
    }

    private String handleOldCmd(byte[] bytes, ChannelHandlerContext ctx) throws Exception {
        int byteBodyLength = bytes[9] & 0xFF;
        logger.info("接收到数据：{}, 转换为字符型则为: {}, 是否为开机数据? {}",
                ByteUtil.byteToHex(bytes), new String(bytes, CommCharset.UTF_8.getValue())
                , true);
//    76 设备号（8字节）03(数据域长度) 23(温度) 35(湿度) 01(翻身为1 不翻为0) D2(校验和)
//    示例数据 76 0D 3C 2A 45 57 0C 0B 03 23 35 01 D2 数据上传
//    76 设备号（8字节）14(数据域长度) 01(表示后面时WIFI账号) 08(wifi账号长度) 23 23 23 23 23 23 23 23(ASCII) 02(表示后面跟的是WIFI密码) 08(WIFI密码长度) 38 38 38 38 38 38 38 38 03(校验和)
//    示例数据 76 0D 3C 2A 45 57 0C 0B 14 01 09 57 41 4e 47 42 49 4e 42 4f 08 31 32 33 34 35 36 37 38 03
//    wifi 设置结果：
//    76 设备号（8字节）01(数据域长度) 01(设置成功为1 设置失败为0) 78(校验和)
        String deviceCode = null;
        byte[] devCode = new byte[8];

        switch (byteBodyLength) {
            case 3:
                System.arraycopy(bytes, 1, devCode, 0, 8);
                deviceCode = ByteUtil.byteToHex(devCode, false);
                logger.info("接收到开机数据, 设备编号:{}, 数据长度: {}, 温度:{}, 湿度:{}, 电压: {}",
                        deviceCode, bytes[9], bytes[10], bytes[11], bytes[12]);
                String wifiPwdIns =
                        RedisUtil.get(RedisCacheEnum.SMART_DEVICE_WIFI, new Object[] {ByteUtil.byteToHex(devCode, false)});
                if (StringUtils.isNotBlank(wifiPwdIns)) {
                    List<String> wifiArr = JSON.parseArray(wifiPwdIns, String.class);
                    if (null == wifiArr || wifiArr.size() != 2) {
                        logger.error("指令接受错误: {}", wifiArr);
                    }
                    logger.info("发送wifi指令： {}", ByteUtil.byteToHex(PiBedInsEnum.generatePasswordIns(ByteUtil.byteToHex(devCode), wifiArr.get(0), wifiArr.get(1))));
                    ctx.writeAndFlush(Unpooled.copiedBuffer(
                            PiBedInsEnum.generatePasswordIns(ByteUtil.byteToHex(devCode), wifiArr.get(0), wifiArr.get(1))));
                }
                break;
            case 1:
                System.arraycopy(bytes, 1, devCode, 0, 8);
                deviceCode = ByteUtil.byteToHex(devCode, false);
                logger.info("接收到WIFI 设置数据, 设备编号:{}, 数据长度: {}, 是否设置成功：{}",
                        ByteUtil.byteToHex(devCode), bytes[9], bytes[10]);
                break;
            default:
                logger.info("未知类型的数据: {}", ByteUtil.byteToHex(devCode));
                break;
        }
        return deviceCode;
    }

    public static void main(String[] args) {
        logger.info("发送wifi指令： {}", ByteUtil.byteToHex(PiBedInsEnum.generateWifiIns("PI", "piaidianzi", "")));
    }
}
